package modules.ASKSender.main

import chisel3._
import chisel3.util._
import modules.ASK.main.DataPackage
import modules.Hamming.HammingEncode
import utils.GlobalConfigLoader.GlobalConfig
import utils.Utils
import utils.Utils.counter

class ASKSender(maxDataBytes: Int = 8)(implicit c: GlobalConfig) extends Module {
  require(maxDataBytes == 8)
  val packedDataInit = VecInit(Seq.fill(8)(0.U(8.W)))
  val packedData = RegInit(packedDataInit)
  val io = IO(new ASKSenderIO(true))
  val hammingEncode = Module(new HammingEncode)
  hammingEncode.io.data := packedData.asTypeOf(hammingEncode.io.data.cloneType)
  val pack = hammingEncode.io.hamming
  val packCnt = RegInit(0.U(log2Ceil(DataPackage.packMaxBits).W))
  val preCnt = RegInit(0.U(log2Ceil(c.ask.preCode.U.getWidth).W))
  val states = Enum(3)
  val stateIdle :: statePreCode :: statePackage :: Nil = states
  val state = RegInit(stateIdle)
  val stateMatrix = Seq(
    stateIdle -> Mux(io.start.get, statePreCode, stateIdle),
    statePreCode -> Mux(preCnt === (c.ask.preCodeWidth - 1).U, statePackage, statePreCode),
    statePackage -> Mux(packCnt === (DataPackage.packMaxBits - 1).U,
      Mux(io.start.get, statePreCode, stateIdle),
      statePackage),
  )
  val stateNext = WireInit(stateIdle)
  stateNext := MuxLookup(state, stateIdle, stateMatrix)
  when(!io.start.get) {
    stateNext := stateIdle
    packCnt := 0.U
    preCnt := 0.U
    packedData := packedDataInit
  }
  state := stateNext

  io.bitOut.get := false.B

  switch(state) {
    is(stateIdle) {
      packedData := packedDataInit
    }
    is(statePreCode) {
      io.bitOut.get := (c.ask.preCode.U >> preCnt).asUInt(0)
      Utils.counter(preCnt, c.ask.preCode.U.getWidth)
      when(stateNext === statePackage) {
        // packedData := Utils.updateVec(packedData, 0.U, io.adcSource)
        // packedData := VecInit(Seq.fill(8)(io.adcSource))
        packedData := io.packSource.get.asTypeOf(packedData)
        // packedData := VecInit(Seq(1.U(8.W)) ++ Seq.fill(7)(io.adcSource))
      }
    }
    is(statePackage) {
      // val dataIndex = (packCnt >> 3.U).asUInt
      // val dataOffset = (packCnt & 7.U).asUInt
      // io.bitOut := (VecInit(Utils.sliceUIntToBytes(pack.asUInt))(dataIndex) >> dataOffset).asUInt(0)
      io.bitOut.get := (pack >> packCnt).asUInt(0)
      Utils.counter(packCnt, DataPackage.packMaxBits)
      // when (packCnt === 55.U) {
      //   io.bitOut := !(pack >> packCnt).asUInt(0)
      // }

      // packedData := Utils.updateVec(packedData, dataIndex + 1.U, io.adcSource)
      // when(dataOffset === 7.U) {
      // }
    }
  }

  io.dacOut := Mux(io.bitOut.get, c.ask.sender.value1.U, c.ask.sender.value0.U)
}

class Sender(maxDataBytes: Int = 8)(implicit c: GlobalConfig) extends Module {
  val io = IO(new ASKSenderIO(false))
  require(io.dacOut.getWidth == 8)

  val senderCnt = RegInit(0.U(log2Ceil(c.ask.clkPerBit).W))
  counter(senderCnt, c.ask.clkPerBit)

  // 调整时钟相位
  val clockOffset = 0.U

  val senderClock = senderCnt >= clockOffset && senderCnt < (c.ask.clkPerBit / 2).U + clockOffset
  withClock(senderClock.asClock) {
    val sender = Module(new ASKSender(maxDataBytes))
    if (io.start.nonEmpty) sender.io.start.get <> io.start.get
    else sender.io.start.get := true.B
    sender.io.dacOut <> io.dacOut
    // sender.io.bitOut.get <> io.bitOut.get
    // set output value
    val valueInit = if (c.ask.sender.constantAdc)
      Seq(0x55, 0xaa, 0x42, 0x30, 0x4, 0x5, 0x6, 0x7) else Seq.fill(8)(0)
    val value = valueInit.map(v => RegInit(v.U(8.W)))
    // val valueIndex = RegInit(0.U(4.W))
    sender.io.packSource.get := VecInit(value).asTypeOf(sender.io.packSource.get.cloneType)
    if (c.ask.sender.constantAdc) {
      // loop value
      // value := (value.head | ((value.asUInt << 8.U).asUInt)).asUInt.asTypeOf(value)
      for (i <- 1 until value.size)
        value(i) := value(i - 1)
      value.head := value.last
    } else {
      // update value from adc
      // for (i <- value.indices) {
      //   when(i.U === valueIndex) {
      //     value(i) := io.adcSource.get
      //   }
      // }
      // only update vec[0]
      value.head := io.adcSource.get
    }
  }
  io.adcClock.get := senderClock
  io.dacClock.get := clock.asUInt
}

class ASKSenderIO(hasPackSource: Boolean)(implicit c: GlobalConfig) extends Bundle {
  val start = if (hasPackSource && !c.ask.sender.autoStart) Some(Input(Bool())) else None
  val packSource = if (hasPackSource) Some(Input(UInt(c.ask.sender.sourceBit.W))) else None
  val dacOut = Output(UInt(c.ask.sender.destBit.W))
  val bitOut = if (hasPackSource) Some(Output(Bool())) else None
  val adcSource = if (!hasPackSource) Some(Input(UInt(c.ask.lengthBit.W))) else None
  val adcClock = if (!hasPackSource) Some(Output(Bool())) else None
  val dacClock = if (!hasPackSource) Some(Output(Bool())) else None
}
